﻿using System.IO;
using System.Data;
using System.Collections.Generic;
using NUnit.Framework;

using System.Net.DnsToolkit.Util;
using System.Net.DnsToolkit.Packet;
using System.Net.DnsToolkit.Packet.Lazy;
using System.Net.DnsToolkit.Serializer.Stream;
using System.Net.DnsToolkit.TestUnit.Helper;
using System.Data.Linq;
using System.Threading;

namespace System.Net.DnsToolkit.TestUnit.Packet.Lazy {
    [TestFixture]
    public class HeaderTest {

        /// <summary>
        /// Domain Name System (query)
        /// Transaction Id: 0x0003
        /// Flags: 0x0100 (Standard query)
        ///    0... .... .... .... = RESPONSE: Message is a query
        ///    .000 0... .... .... = Opcode: Standard query (0)
        ///    .... .0.. .... .... = Authoritative: Server is not an authority for domain
        ///    .... ..0. .... .... = Truncated: Message is not truncated
        ///    .... ...1 .... .... = Recursion desired: Do query recursively
        ///    .... .... 0... .... = Recursion available: No
        ///    .... .... .0.. .... = Z: reserved (0)
        ///    .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server
        ///    .... .... ...0 .... = Non-authenticated data OK: Non-authenticated data is unacceptable
        ///    .... .... .... 0000 = Reply code: No error (0)
        /// Questions: 1
        /// Answer RRs: 0
        /// Authority RRs: 0
        /// Additional RRs: 0
        /// Queries
        ///    www.domn.tl: type A, class In
        ///       Name: www.domn.tl
        ///       Type: A (Host address)
        ///       Class: In (0x0001)
        /// </summary>
        [Test]
        public void Test1() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            using (var mem = new MemoryStream(ss)) {

                var bufferReader = new BufferReader(mem);

                var lazyReader = new System.Net.DnsToolkit.Packet.Lazy.HeaderReader();

                IHeader header = lazyReader.Read(bufferReader);
                Assert.AreEqual(0x3, header.Id);
                Assert.AreEqual(QREnum.QUERY, header.QueryType);
                Assert.AreEqual(OpCodeEnum.StandardQuery, header.OpCode);
                Assert.AreEqual(false, header.AuthoritativeAnswer);
                Assert.AreEqual(false, header.Truncation);
                Assert.AreEqual(true, header.RecursionDesired);
                Assert.AreEqual(false, header.RecursionAvailable);
                Assert.AreEqual(false, header.Reserved);
                Assert.AreEqual(false, header.AnswerAuthenticated);
                Assert.AreEqual(false, header.NotAuthenticatedAcceptable);
                Assert.AreEqual(RCodeEnum.NoError, header.ResponseCode);
                Assert.AreEqual(1, header.QuestionCount);
                Assert.AreEqual(0, header.AnswerCount);
                Assert.AreEqual(0, header.AuthorityCount);
                Assert.AreEqual(0, header.ResourceCount);
            }


        }

        [Test]
        public void TestRamdom() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            using (var mem = new MemoryStream(ss)) {

                var bufferReader = new BufferReader(mem);

                var lazyReader = new System.Net.DnsToolkit.Packet.Lazy.HeaderReader();

                IHeader header = lazyReader.Read(bufferReader);
                Assert.AreEqual(0, header.ResourceCount);
                Assert.AreEqual(0, header.AuthorityCount);
                Assert.AreEqual(0, header.AnswerCount);
                Assert.AreEqual(1, header.QuestionCount);
                Assert.AreEqual(RCodeEnum.NoError, header.ResponseCode);
                Assert.AreEqual(false, header.NotAuthenticatedAcceptable);
                Assert.AreEqual(false, header.AnswerAuthenticated);
                Assert.AreEqual(false, header.Reserved);
                Assert.AreEqual(false, header.RecursionAvailable);
                Assert.AreEqual(true, header.RecursionDesired);
                Assert.AreEqual(false, header.Truncation);
                Assert.AreEqual(false, header.AuthoritativeAnswer);
                Assert.AreEqual(OpCodeEnum.StandardQuery, header.OpCode);
                Assert.AreEqual(QREnum.QUERY, header.QueryType);
                Assert.AreEqual(0x3, header.Id);
            }


        }
        [Test]
        public void TestCompare() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            using (var mem = new MemoryStream(ss)) {

                var bufferReader = new BufferReader(mem);

                var lazyReader = new System.Net.DnsToolkit.Packet.Lazy.HeaderReader();
                var busyReader = new System.Net.DnsToolkit.Serializer.Stream.Reader.HeaderReader();

                IHeader lazyHeader = lazyReader.Read(bufferReader);
                IHeader busyHeader = busyReader.Read(mem);

                MessageHelper.DeepCompare(busyHeader, lazyHeader);

            }

        }

        [Test]
        public void TestCompare100000Lazy() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            using (var mem = new MemoryStream(ss)) {

                var bufferReader = new BufferReader(mem);
                var reader = new System.Net.DnsToolkit.Packet.Lazy.HeaderReader();
  
                for (int i = 0; i < 10000000; i++) {

                    IHeader header = reader.Read(bufferReader);
                    
                    Assert.AreEqual(QREnum.QUERY, header.QueryType);               
                    Assert.AreEqual(OpCodeEnum.StandardQuery, header.OpCode);
                    Assert.AreEqual(true, header.RecursionDesired);
                    Assert.AreEqual(RCodeEnum.NoError, header.ResponseCode);
                    Assert.AreEqual(1, header.QuestionCount);
                    Assert.AreEqual(0, header.AnswerCount);
                    Assert.AreEqual(0, header.AuthorityCount);
                    Assert.AreEqual(0, header.ResourceCount);

                }
            }

        }

        [Test]
        public void TestCompare100000Busy() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            using (var mem = new MemoryStream(ss)) {

                var reader = new System.Net.DnsToolkit.Serializer.Stream.Reader.HeaderReader();

                for (int i = 0; i < 10000000; i++) {
                    mem.Seek(0, SeekOrigin.Begin);
                    IHeader header = reader.Read(mem);

                    Assert.AreEqual(QREnum.QUERY, header.QueryType);
                    Assert.AreEqual(OpCodeEnum.StandardQuery, header.OpCode);
                    Assert.AreEqual(true, header.RecursionDesired);
                    Assert.AreEqual(RCodeEnum.NoError, header.ResponseCode);
                    Assert.AreEqual(1, header.QuestionCount);
                    Assert.AreEqual(0, header.AnswerCount);
                    Assert.AreEqual(0, header.AuthorityCount);
                    Assert.AreEqual(0, header.ResourceCount);

                }
            }

        }




        [Test]
        public void TestCompareParallele100000Lazy() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);

            for (int i = 0; i < 1000000; i++) {

                var mem = new MemoryStream(ss);
                var reader = new System.Net.DnsToolkit.Packet.Lazy.HeaderReader();
                var bufferReader = new BufferReader(mem);

                IHeader header = reader.Read(bufferReader);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(QREnum.QUERY, ((IHeader)h).QueryType), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(OpCodeEnum.StandardQuery, ((IHeader)h).OpCode), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(true, ((IHeader)h).RecursionDesired), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(RCodeEnum.NoError, ((IHeader)h).ResponseCode), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(1, ((IHeader)h).QuestionCount), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(0, ((IHeader)h).AnswerCount), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(0, ((IHeader)h).AuthorityCount), header);
                ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(0, ((IHeader)h).ResourceCount), header);


            }


        }

        [Test]
        public void TestCompareParallele100000Busy() {

            const string source = "0003010000010000000000000377777704646F6D6E02746C0000010001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            using (var mem = new MemoryStream(ss)) {

                var reader = new System.Net.DnsToolkit.Serializer.Stream.Reader.HeaderReader();

                for (int i = 0; i < 1000000; i++) {
                    mem.Seek(0, SeekOrigin.Begin);
                    IHeader header = reader.Read(mem);

                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(QREnum.QUERY, ((IHeader)h).QueryType), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(OpCodeEnum.StandardQuery, ((IHeader)h).OpCode), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(true, ((IHeader)h).RecursionDesired), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(RCodeEnum.NoError, ((IHeader)h).ResponseCode), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(1, ((IHeader)h).QuestionCount), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(0, ((IHeader)h).AnswerCount), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(0, ((IHeader)h).AuthorityCount), header);
                    ThreadPool.QueueUserWorkItem(h => Assert.AreEqual(0, ((IHeader)h).ResourceCount), header);



                }
            }

        }
    }
}
